/*****************************************************************************
* @author  : windsPx                                                         *
* @date    :                                                                 *
* @file    :                                                                 *
* @brief   :                                                                 *
*----------------------------------------------------------------------------*
* Date        | Version   | Author         | Description                     *
*----------------------------------------------------------------------------*
*             |           |  windsPx              |                          *
*****************************************************************************/

#pragma once

#include "Ringbuffer.h"
#include <mutex>
#include <array>

namespace _disruptor
{
//消息这通过消费者代理访问ring
template <typename BUFF, typename BLOCKSTRATEGY, int EleCnt = 16>
class CBaseCustomProxy : public CCustomFace
{
protected:
	//！关联的ringbuffer
	BUFF* m_ref_ring;
	//! 每个customer对应的当前最大可访问序号
	struct OrderRingIdx
	{
		char pad0[56] = { 0 };
		RINGINDEX m_index = 0;
		char pad1[56] = { 0 };
	};
//	std::vector<OrderRingIdx > m_customer_order_nums;
	std::array<OrderRingIdx, EleCnt> m_customer_order_nums;
	char pad[64] = { 0 };
	int m_ele_cnt = 0;
	//! 阻塞策略
//	std::shared_ptr<CBlockBaseStrategy> m_block;
    std::shared_ptr<BLOCKSTRATEGY> m_block;
public:
    CBaseCustomProxy(BUFF* ring, std::shared_ptr<BLOCKSTRATEGY> block_policy)
		: m_ref_ring(ring), m_block(block_policy)
	{
	}
	~CBaseCustomProxy() {}
public:
	virtual int getMemType() { return -1; }
public:
	virtual void initCustomer(int custom_id)
	{
	//	m_customer_order_nums.push_back(OrderRingIdx());
		++m_ele_cnt;
		assert(m_ele_cnt <= EleCnt);
		m_block->init(custom_id);
	}
	void addCustomer(int custom_id, RINGINDEX order_num)
	{
		m_customer_order_nums[custom_id].m_index = order_num;
	}
	RINGINDEX getOrderNum(int custom_id)
	{
		return m_ref_ring->MaxOrderNum();
	}
public:
	//是否把之前的都消费完了
	RINGINDEX minOrderNum()
	{
		RINGINDEX min_order = m_customer_order_nums[0].m_index;
		for (int i = 0; /*i < (int)m_customer_order_nums.size();*/ i < m_ele_cnt; ++i)
		{
			if (m_customer_order_nums[i].m_index < min_order)
				min_order = m_customer_order_nums[i].m_index;
		}
		return min_order;
	}
	int customerNum()
	{
//		return m_customer_order_nums.size();
        return m_ele_cnt;
	}
	//唤醒
	void Wakeup()
	{
		m_block->wakeup();
	}
protected:
	//等待
	void Wait(int custom_id)
	{
		m_block->wait(custom_id);
	}
};

template <typename Cli, typename BUFF, typename BLOCKSTRATEGY, int MemType>
class CCustomProxy;
//申明成某类消费者的消费代理
template <typename Cli, typename BUFF, typename BLOCKSTRATEGY>
class CCustomProxy<Cli, BUFF, BLOCKSTRATEGY, BaseMem> : public CBaseCustomProxy<BUFF, BLOCKSTRATEGY>
{
public:
    CCustomProxy(BUFF* ring, std::shared_ptr<BLOCKSTRATEGY> block_policy)
        : CBaseCustomProxy<BUFF, BLOCKSTRATEGY>(ring, block_policy)
	{}
	~CCustomProxy() {}
public:
	int getMemType() override
	{
		return BaseMem;
	}
	void readData(Cli* customer)
	{
		int custom_id = customer->GetId();
		RINGINDEX custom_order_num = customer->GetCurOrderNum();
		//先访问获取ring中最大序号
		RINGINDEX order_num = 0;
		do
		{
            order_num = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::getOrderNum(custom_id);
			if (custom_order_num < order_num)               //小于ring中最大的游标号 则表示可读
			{
				break;
			}
			//这里要wait for : could read next valid order-num
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::Wait(custom_id);
		} while (1);
		//从当前序号一直到最大序号取数据
		for (RINGINDEX i = custom_order_num; i < order_num; ++i)
		{
            typename BUFF::BUFEntry* entry = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::m_ref_ring->GetEntry(i);
			//业务层必须提供的接口 ---- 如果处理流程慢 则容易造成阻塞
			customer->customData(&entry, i);
			//更新当前序号
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, i);
		}
		//访问完了 更新序号
		customer->setCurOrderNum(order_num);
		//更新消费里的序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, order_num);
	}
};

//消费者一次只消费一个对象
template <typename Cli, typename BUFF, typename BLOCKSTRATEGY>
class CCustomProxy<Cli, BUFF, BLOCKSTRATEGY, ObjMemPtr> : public CBaseCustomProxy<BUFF, BLOCKSTRATEGY>
{
public:
    CCustomProxy(BUFF* ring, std::shared_ptr<BLOCKSTRATEGY> block_policy)
        : CBaseCustomProxy<BUFF, BLOCKSTRATEGY>(ring, block_policy)
	{}
	~CCustomProxy() {}
	typedef typename BUFF::BUFEntry::OBJType Cli_T;
public:
	int getMemType() override
	{
		return ObjMemPtr;
	}
	//customer提供回调
	void readData(Cli* customer)
	{
		int custom_id = customer->GetId();
		RINGINDEX custom_order_num = customer->GetCurOrderNum();
		//先访问获取ring中最大序号
		RINGINDEX order_num = 0;
		do
		{
            order_num = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::getOrderNum(custom_id);
			if (custom_order_num < order_num)               //小于ring中最大的游标号 则表示可读
			{
				break;
			}
			//这里要wait for : could read next valid order-num
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::Wait(custom_id);
		} while (1);
		//从当前序号一直到最大序号取数据
		for (RINGINDEX i = custom_order_num; i < order_num; ++i)
		{
            typename BUFF::BUFEntry* entry = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::m_ref_ring->GetEntry(i);
			//待处理的对象指针 -- 若使用模式 业务层必须提供的接口
			std::shared_ptr<Cli_T> obj_ptr = entry->GetInnerPtr();
			//消费 -- 若使用模式 业务层必须提供的接口
			customer->customData(obj_ptr, i);
			//更新当前序号
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, i);
		}
		//访问完了 更新序号
		customer->setCurOrderNum(order_num);
		//更新消费里的序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, order_num);
	}

	//根据customer 去取一个
	std::shared_ptr<Cli_T> readOneData(Cli* customer)
	{
		int custom_id = customer->GetId();
		RINGINDEX custom_order_num = customer->GetCurOrderNum();
		//先访问获取ring中最大序号
		RINGINDEX order_num = 0;
		do
		{
            order_num = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::getOrderNum(custom_id);
			if (custom_order_num < order_num)               //小于ring中最大的游标号 则表示可读
			{
				break;
			}
			//这里要wait for : could read next valid order-num
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::Wait(custom_id);
		} while (1);
		//取一个
        typename BUFF::BUFEntry* entry = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::m_ref_ring->GetEntry(custom_order_num);
		//待处理的对象指针 -- 若使用模式 业务层必须提供的接口
		std::shared_ptr<Cli_T> obj_ptr = entry->GetInnerPtr();
		//更新当前序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, custom_order_num);
		//访问完了 更新序号
		customer->setCurOrderNum(custom_order_num + 1);
		//更新消费里的序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, custom_order_num + 1);
		return obj_ptr;
	}
};

//消费者一次消费多个对象
template <typename Cli, typename BUFF, typename BLOCKSTRATEGY>
class CCustomProxy<Cli, BUFF, BLOCKSTRATEGY, ObjBatchPtr> : public CCustomProxy<Cli, BUFF, BLOCKSTRATEGY, ObjMemPtr>
{
public:
    CCustomProxy(BUFF* ring, std::shared_ptr<BLOCKSTRATEGY> block_policy)
        : CCustomProxy<Cli, BUFF, BLOCKSTRATEGY, ObjMemPtr>(ring, block_policy)
	{
	}
	~CCustomProxy() {}

	typedef typename BUFF::BUFEntry::OBJType Cli_T;
	typedef std::list<std::shared_ptr<Cli_T>> CustomList;
public:
	int getMemType() override
	{
		return ObjBatchPtr;
	}
	//customer提供回调
	void readData(Cli* customer)
	{
		int custom_id = customer->GetId();
		RINGINDEX custom_order_num = customer->GetCurOrderNum();
		//先访问获取ring中最大序号
		RINGINDEX order_num = 0;
		do
		{
            order_num = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::getOrderNum(custom_id);
			if (custom_order_num < order_num)               //小于ring中最大的游标号 则表示可读
			{
				break;
			}
			//这里要wait for : could read next valid order-num
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::Wait(custom_id);
		} while (1);
		//从当前序号一直到最大序号取数据
		CustomList obj_list;
		for (RINGINDEX i = custom_order_num; i < order_num; ++i)
		{
            typename BUFF::BUFEntry* entry = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::m_ref_ring->GetEntry(i);
			//待处理的对象指针 -- 若使用模式 业务层必须提供的接口
			std::shared_ptr<Cli_T> obj_ptr = entry->GetInnerPtr();
			obj_list.emplace_back(obj_ptr);
			//更新当前序号
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, i);
		}
		//访问完了 更新序号
		customer->setCurOrderNum(order_num);
		//更新消费里的序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, order_num);
		//消费 -- 若使用模式 业务层必须提供的接口
		customer->customData(obj_list, order_num);
	}

	//根据customer取出
	void readData(Cli* customer, CustomList& obj_list)
	{
		int custom_id = customer->GetId();
		RINGINDEX custom_order_num = customer->GetCurOrderNum();
		//先访问获取ring中最大序号
		RINGINDEX order_num = 0;
		do
		{
            order_num = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::getOrderNum(custom_id);
			if (custom_order_num < order_num)               //小于ring中最大的游标号 则表示可读
			{
				break;
			}
			//这里要wait for : could read next valid order-num
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::Wait(custom_id);
		} while (1);
		//从当前序号一直到最大序号取数据
		for (RINGINDEX i = custom_order_num; i < order_num; ++i)
		{
            typename BUFF::BUFEntry* entry = CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::m_ref_ring->GetEntry(i);
			//待处理的对象指针 -- 若使用模式 业务层必须提供的接口
			std::shared_ptr<Cli_T> obj_ptr = entry->GetInnerPtr();
			obj_list.emplace_back(obj_ptr);
			//更新当前序号
            CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, i);
		}
		//访问完了 更新序号
		customer->setCurOrderNum(order_num);
		//更新消费里的序号
        CBaseCustomProxy<BUFF, BLOCKSTRATEGY>::addCustomer(custom_id, order_num);
	}
};
}
